require("scripts/Atmosphere_libs/Helper")
require("scripts/Math/vector")

CSkymanager = class(function(s) 
	s.Initialized = 0
	s.m_nSamples = 5
	s.m_Kr = 0.0025
	s.m_Kr4PI = s.m_Kr*4.0*math.pi
	s.m_Km = 0.0015
	s.m_Km4PI = s.m_Km*4.0*math.pi
	s.m_ESun = 5
	s.m_g = -0.98
	
	s.m_fInnerRadius = 10000.0
	s.m_fOuterRadius = s.m_fInnerRadius * 1.25
	s.m_fScale = 1.0 / (s.m_fOuterRadius - s.m_fInnerRadius)

	s.m_fWavelength = Vec3() 
	s.m_fWavelength4 = Vec3()

	s.m_fWavelength.x = 0.650
	s.m_fWavelength.y = 0.570
	s.m_fWavelength.z = 0.475
	s.m_fWavelength4.x = (s.m_fWavelength.x^4.0)
	s.m_fWavelength4.y = (s.m_fWavelength.y^4.0)
	s.m_fWavelength4.z = (s.m_fWavelength.z^4.0)

	s.m_fRayleighScaleDepth = 0.9
	s.m_fMieScaleDepth = 0.45

	s.starcolors = { Vec3(255, 224, 192),Vec3(255, 255, 192),Vec3(192, 224, 255),Vec3(255, 255, 255)}
	s.numberofstars = 0
	s.starmaterial = LoadMaterial("abstract::star.mat")
end)

function CSkymanager:Free()
	self.sunlight:Free()
	self.starmesh:Free()
	self.moonmesh:Free()
	fw.renderer:SetSkybox(nil)
	
	if self.nightbuffer ~= nil then
		self.nightbuffer:Free()
	end

	self.Initialized = 0
end

function CSkymanager:Init()
	if self.Initialized == 0 then
		SaveWorldSettings()

		self.sunlight = CreateDirectionalLight()
		RotateEntity(self.sunlight,Vec3(175,0,0))
		--self.moonlight = CreateDirectionalLight()

		self:SetupScattering()	
		self:SetupNightSky()
		self.Initialized = 1

		RestoreWorldSettings()

	else
		self:Free()
		self:Init()
	end
end 

function CSkymanager:Update(lightrotation, moonrotation, intensity)
	self:RefreshBuffers()

	if self.Initialized == 0 then
		self:Init()
	end

	if lightrotation ~=nil then
		RotateEntity(self.sunlight,lightrotation)
	else
		RotateEntity(self.sunlight,Vec3(20,0,0))
	end

	if moonrotation ~=nil then
		RotateEntity(self.moonpivot,Vec3(moonrotation.x,moonrotation.y,0))
	else
		RotateEntity(self.moonpivot,Vec3(60,0,0))
	end

	if fw ~= nil then
		self:UpdateShaderValues(intensity)
		fw.renderer:SetSkybox(self.skymaterial)	
		
		SaveWorldSettings()
		if self.nightbuffer ~= nil then
		--	SetBuffer(self.nightbuffer)
		--	SetWorld(self.nightworld) 
		--	PositionEntity(self.nightcamera, Vec3(0, 0, 0))
		--	CameraRange(self.nightcamera, 0.1, 1000.0)
		--	self.nightcamera:SetRotation(EntityRotation(fw.main.camera, 1))
		--	self.nightworld:Render() 
		end

		RestoreWorldSettings()
	end

end

function CSkymanager:RefreshBuffers()
	
end 

function CSkymanager:SetKey(key,value)
	if key == 'atm.samples' then 
		self.m_nSamples = tonumber(value)
	elseif key == 'atm.kr' then
		self.m_Kr = tonumber(value)
	elseif key == 'atm.km' then
		self.m_Km = tonumber(value)
	elseif key == 'atm.esun' then
		self.m_ESun = tonumber(value)
	elseif key == 'atm.g' then
		self.m_g = tonumber(value)
	elseif key == 'atm.radius' then
		self.m_fInnerRadius = tonumber(value)
	elseif key == 'atm.wavelength' then
		self.m_fWavelength = StringToVec3(value)
	elseif key == 'atm.mie' then
		self.m_fMieScaleDepth = tonumber(value)
	elseif key == 'atm.rayleigh' then
		self.m_fRayleighScaleDepth = tonumber(value)
	end
	
	self:RefreshValues()
	
	return 1
end

function CSkymanager:RefreshValues()
	self.m_Kr4PI = self.m_Kr*4.0*math.pi
	self.m_Km4PI = self.m_Km*4.0*math.pi

	self.m_fOuterRadius = self.m_fInnerRadius * 1.125
	self.m_fScale = 1.0 / (self.m_fOuterRadius - self.m_fInnerRadius)

	self.m_fWavelength4.x = (self.m_fWavelength.x^4.0)
	self.m_fWavelength4.y = (self.m_fWavelength.y^4.0)
	self.m_fWavelength4.z = (self.m_fWavelength.z^4.0)	
end

function CSkymanager:GetKey(key,value)
	if key == 'atm.samples' then 
		return self.m_nSamples
	elseif key == 'atm.kr' then
		return self.m_Kr
	elseif key == 'atm.km' then
		return self.m_Km
	elseif key == 'atm.esun' then
		return self.m_ESun
	elseif key == 'atm.g' then
		return self.m_g 
	elseif key == 'atm.radius' then
		return self.m_fInnerRadius
	elseif key == 'atm.mie' then
		return self.m_fMieScaleDepth 
	elseif key == 'atm.rayleigh' then
		return self.m_fRayleighScaleDepth 
	elseif key == 'atm.wavelength' then
		return self.m_fWavelength.x..","..self.m_fWavelength.y..","..self.m_fWavelength.z
	end
end

function CSkymanager:SetupScattering()
	self.skymaterial = CreateMaterial()
	self.scatteringshader = LoadShader("abstract::scattersky.vert","abstract::scattersky.frag")
	SetMaterialShader(self.skymaterial,self.scatteringshader)
	SetMaterialTexture(self.skymaterial,LoadTexture("abstract::nightsky1.dds"),0)
end

function CSkymanager:UpdateShaderValues(intensity)
	local lightdir = TFormNormal(Vec3(0, 0, -1) , self.sunlight, nil)
	local filter_scattering = self.scatteringshader
	local campos = Vec3(0 , self.m_fInnerRadius + 2000.0, 0)
	SetShaderVec3(filter_scattering, "v3campos", campos)
	SetShaderVec3(filter_scattering, "v3LightPos", lightdir)
	SetShaderVec3(filter_scattering,"v3InvWavelength", Vec3(1.0/self.m_fWavelength4.x, 1.0/self.m_fWavelength4.y, 1.0/self.m_fWavelength4.z))
	SetShaderFloat(filter_scattering,"fCameraHeight", campos.y)
	SetShaderFloat(filter_scattering,"fCameraHeight2",  campos.y * campos.y)
	SetShaderFloat(filter_scattering, "fInnerRadius", self.m_fInnerRadius)
	SetShaderFloat(filter_scattering, "fInnerRadius2", self.m_fInnerRadius * self.m_fInnerRadius)
	SetShaderFloat(filter_scattering,"fOuterRadius", self.m_fOuterRadius)
	SetShaderFloat(filter_scattering,"fOuterRadius2", self.m_fOuterRadius*self.m_fOuterRadius)
	SetShaderFloat(filter_scattering,"fKrESun", self.m_Kr*self.m_ESun)
	SetShaderFloat(filter_scattering,"fKmESun", self.m_Km*self.m_ESun)
	SetShaderFloat(filter_scattering,"fKr4PI", self.m_Kr4PI)
	SetShaderFloat(filter_scattering,"fKm4PI", self.m_Km4PI)
	SetShaderFloat(filter_scattering, "fScale", self.m_fScale)
	SetShaderFloat(filter_scattering,"fScaleDepth", self.m_fRayleighScaleDepth )
	SetShaderFloat(filter_scattering, "fScaleOverScaleDepth", self.m_fScale / self.m_fRayleighScaleDepth)
	SetShaderFloat(filter_scattering,"g", self.m_g)
	SetShaderFloat(filter_scattering,"g2", self.m_g*self.m_g)
	SetShaderInt(filter_scattering,"nSamples", self.m_nSamples)
	SetShaderFloat(filter_scattering, "fSamples", self.m_nSamples)
	SetShaderFloat(filter_scattering, "fintensity", intensity)
	SetShaderFloat(GetMaterialShader(self.moontexture), "fintensity", 1.0-intensity)
end

function CSkymanager:SetupNightSky()
		
	SetWorld(fw.background.world)

	self:CreateStars()
	
	self.moontexture = LoadMaterial("abstract::moon.mat")
	self.moonpivot = CreatePivot()

	local mesh = CreateMesh()
	local surf = CreateSurface(mesh)
	local s = 15.0

	surf:AddVertex (Vec3(-s, s, 0), Vec3(0, 0, 0), Vec2(1, 0))
	surf:AddVertex (Vec3(s, s, 0), Vec3(0, 0, 0), Vec2(0, 0))
	surf:AddVertex (Vec3(-s, -s, 0), Vec3(0, 0, 0), Vec2(1, 1))
	surf:AddVertex (Vec3(s, -s, 0), Vec3(0, 0, 0), Vec2(0, 1))
	
	surf:SetVertexColor(0, 255.0, 255.0, 255.0, 255.0)
	surf:SetVertexColor(1, 255.0, 255.0, 255.0, 255.0)
	surf:SetVertexColor(2, 255.0, 255.0, 255.0, 255.0)
	surf:SetVertexColor(3, 255.0, 255.0, 255.0, 255.0)
	
	surf:AddTriangle(0, 1, 2)
	surf:AddTriangle(3, 2, 1)
	
	PaintEntity(mesh, self.moontexture)
		
	UpdateMesh(mesh)
	EntityViewRange(mesh, 3)
	PositionEntity(mesh, Vec3(0, 0, -75), 1)
	EntityParent(mesh, self.moonpivot)
	FlipMesh(mesh)
	
	self.moonmesh = mesh
	EntityOrder(self.moonmesh,1000,1)
end

function CSkymanager:CreateStars()
	if self.starmesh ~= nil then
		FreeEntity(self.starmesh)
	end
	
	self.starmesh = CreateMesh()
	local surface = CreateSurface(self.starmesh)
	math.randomseed(os.time())
	for i = 0,self.numberofstars do
	
		local color = self.starcolors[math.random(1,4)]
		CreateStar(surface,color,math.random(10,85),math.random(0,359),250)
		Print(i)
	end

	PaintEntity(self.starmesh, self.starmaterial)
	EntityViewRange(self.starmesh, 3)
	UpdateMesh(self.starmesh)
	EntityOrder(self.starmesh,0,1)
end 

